namespace utils {
    export class Vec2D {
        public x: number
        public y: number
        public Kind: number
        private shape: egret.Shape
        constructor(x, y: number) {
            this.x = x
            this.y = y
        }
        public Clone(): Vec2D {
            return new Vec2D(this.x, this.y)
        }

        public Atan2(): number {
            let r = Math.atan2(this.x, this.y)
            if (r < 0) {
                r += 2 * Math.PI
            }
            return r
        }

        public SetLoc(x, y: number) {
            this.x = x
            this.y = y
            if (this.shape) {
                this.shape.x = this.x
                this.shape.y = this.y
            }
        }
        public Draw(): egret.Shape {
            if (this.shape == null) {
                this.shape = new egret.Shape()
                if (ShadowPoint == this.Kind) {
                    this.shape.graphics.beginFill(Color.YELLOW)
                } else {
                    this.shape.graphics.beginFill(Color.RED)
                }
                this.shape.graphics.drawCircle(0, 0, 5)
                this.shape.graphics.endFill()
                this.shape.x = this.x
                this.shape.y = this.y
            }
            return this.shape
        }
        public Erase() {
            if (this.shape && this.shape.parent) {
                this.shape.parent.removeChild(this.shape)
                this.shape = null
            }
        }
        public Equal(other: Vec2D): boolean {
            return Equal(this.x, other.x) && Equal(this.y, other.y)
        }
    }

    export class Circle {
        private center: Vec2D
        private radius: number
        private shape: egret.Shape
        constructor(x, y, r: number) {
            this.center = new Vec2D(x, y)
            this.radius = r
        }
        public Draw(): egret.Shape {
            if (this.shape == null) {
                this.shape = new egret.Shape()
                this.shape.graphics.lineStyle(1, Color.RED)
                this.shape.graphics.drawCircle(0, 0, this.radius)
                this.shape.x = this.center.x
                this.shape.y = this.center.y
            }
            return this.shape
        }
        public SetLoc(x, y: number) {
            this.center.SetLoc(x, y)
            if (this.shape) {
                this.shape.x = this.center.x
                this.shape.y = this.center.y
            }
        }
        public get Loc(): utils.Vec2D {
            return this.center
        }
    }

    export const Normal = 0
    export const Border = 1
    export const Stone = 2
    export const Eye = 3
    export const HideLine = 4
    export const ShadowPoint = 5
    export const ShadowPolygon = 5

    export function Equal(a: number, b: number) {
        return Math.abs(a - b) < 0.001
    }

    export function SubVec2(a: Vec2D, b: Vec2D) {
        return new Vec2D(a.x - b.x, a.y - b.y)
    }

    export class Polygon {
        public Points: Array<Vec2D>
        public Edges: Array<Line>
        private shape: egret.Shape
        public Kind: number
        constructor(kind: number, ...points: Array<Vec2D>) {
            this.Kind = kind
            this.SetPoints(points)
        }
        public SetPoints(points: Array<Vec2D>) {
            if (!points) return
            this.Points = points
            this.Edges = new Array<Line>()
            let lastPoint = this.Points[this.Points.length - 1]
            this.Points.forEach((p) => {
                this.Edges.push(new Line(lastPoint, p))
                lastPoint = p
            })
        }
        public Draw(): egret.Shape {
            if (this.shape == null) {
                this.shape = new egret.Shape()
                let lastPoint = this.Points[this.Points.length - 1]
                switch (this.Kind) {
                    case Stone:
                        this.shape.graphics.lineStyle(2, Color.BLACK)
                        this.shape.graphics.beginFill(Color.CYAN)
                        break
                    case ShadowPolygon:
                        this.shape.graphics.lineStyle(0, Color.GRAY)
                        this.shape.graphics.beginFill(Color.BLACK, 0.3)
                        break
                    default:
                        this.shape.graphics.lineStyle(2, Color.BLACK)
                        this.shape.graphics.beginFill(Color.GRAY)
                        break
                }
                this.shape.graphics.moveTo(lastPoint.x, lastPoint.y)
                this.Points.forEach((p) => {
                    this.shape.graphics.lineTo(p.x, p.y)
                })
                this.shape.graphics.endFill()
            }
            return this.shape
        }
        public Erase() {
            if (this.shape && this.shape.parent) {
                this.shape.parent.removeChild(this.shape)
                this.shape = null
            }
        }
    }

    export class Line {
        private shape: egret.Shape
        private from: Vec2D
        private to: Vec2D
        private a: number
        private b: number
        private c: number
        public Kind: number
        public get To(): Vec2D {
            return this.to
        }
        public get From(): Vec2D {
            return this.from
        }
        constructor(from, to: Vec2D) {
            this.from = from
            this.to = to

            if (Equal(this.from.x - this.to.x, 0)) {
                this.a = 1
                this.b = 0
            } else {
                this.a = -(this.from.y - this.to.y) / (this.from.x - this.to.x)
                this.b = 1
            }
            this.c = -(this.a * this.from.x + this.b * this.from.y)
        }

        public IsParallel(line: Line): boolean {
            return Math.abs(this.a - line.a) < 0.001
        }
        public Cross(line: Line, direct, limit1, limit2: boolean): Vec2D {
            if (this.IsParallel(line)) {
                return null
            }
            let x = 0
            let y = 0
            if (!Equal(line.b, 0)) {
                x = -((this.c / this.b - line.c / line.b) / (this.a / this.b - line.a / line.b))
                y = -(line.c / line.b + (line.a / line.b) * x)
            } else {
                y = -((this.c / this.a - line.c / line.a) / (this.b / this.a - line.b / line.a))
                x = -(line.c / line.a + (line.b / line.a) * y)
            }
            let p = new Vec2D(x, y)
            if (direct && !Equal(SubVec2(this.to, this.from).Atan2(), SubVec2(p, this.from).Atan2())) {
                return null
            }
            if (limit1 && !this.OnLine(p)) {
                return null
            }
            if (limit2 && !line.OnLine(p)) {
                return null
            }
            return p
        }
        public OnLine(p: Vec2D): boolean {
            /*
            if (this.from.x > this.to.x) {
                if (p.x > this.from.x || p.x < this.to.x) {
                    return false
                }
            } else {
                if (p.x > this.to.x || p.x < this.from.x) {
                    return false
                }
            }
             if (this.from.y > this.to.y) {
                if (p.y > this.from.y || p.y < this.to.y) {
                    return false
                }
            } else {
                if (p.y > this.to.y || p.y < this.from.y) {
                    return false
                }
            }
            return true
            */
            if (!Equal(this.a * p.x + this.b * p.y + this.c, 0)) {
                return false
            }
            if (Math.abs(p.x - (this.from.x + this.to.x) / 2) > Math.abs(this.from.x - this.to.x) / 2) {
                return false
            }
            if (Math.abs(p.y - (this.from.y + this.to.y) / 2) > Math.abs(this.from.y - this.to.y) / 2) {
                return false
            }
            return true
        }

        public Draw(): egret.Shape {
            if (this.shape == null) {
                this.shape = new egret.Shape()
                if (HideLine == this.Kind) {
                    this.shape.graphics.lineStyle(1, Color.GREEN)
                } else {
                    this.shape.graphics.lineStyle(1, Color.RED)
                }
                this.shape.graphics.moveTo(this.from.x, this.from.y)
                this.shape.graphics.lineTo(this.to.x, this.to.y)
            }
            return this.shape
        }
        public Erase() {
            if (this.shape && this.shape.parent) {
                this.shape.parent.removeChild(this.shape)
                this.shape = null
            }
        }

    }
}